import {
  BaseCommand,
  DualController,
  ITransformDetail,
  pickedFaceData,
} from '@amcax/base';
import { applyMatrix4ArrayToObject } from '@amcax/renderer';
import {
  NumberInputFormat,
  PickInputRJSFFormat,
} from '@app-base/common/components/command-form/constants';
import { PickableCommandForm } from '@app-base/common/components/command-form/pickable';
import { useCommand } from '@app-base/common/hooks/useCommand';
import { throttledShowWarning } from '@app-base/common/utils';
import { ctx2, userEvents } from '@app-base/module';
import { i18n } from '@app-nextmesh/common/utils';
import { RJSFSchema, UiSchema } from '@rjsf/utils';
import { useEffect, useRef, useState } from 'react';
import {
  BufferAttribute,
  BufferGeometry,
  DoubleSide,
  Line,
  LineBasicMaterial,
  Matrix4,
  Mesh,
  MeshBasicMaterial,
  Plane,
  PlaneGeometry,
  Quaternion,
  Vector3,
} from 'three-legacy';
import { allParts } from '../../../../pages/product-support/part-funcs';
import './index.scss';
import { AxisType, SectionFormDataType } from './type';

export const SectionViewActivatedEvent = 'sectionViewActivated';

function SectionView({ title, onClose }) {
  const [formTouched, setFormTouched] = useState(false);
  const [formData, setFormData] = useState<SectionFormDataType>({
    plane: null,
    distance: 0,
    uAngle: 0,
    vAngle: 0,
    reverse: false,
  });
  const width = 50;
  const height = 50;
  // 监听是否通过表单来改变distance 的值
  const [distanceDiff, setDistanceDiff] = useState(0);
  const [uAngleDiff, setUAngleDiff] = useState(0);
  const [vAngleDiff, setVAngleDiff] = useState(0);
  const [axisSmallestArray, setAxisSmallestArray] = useState<string[]>([]);
  const planeNormalRef = useRef<Vector3>(null);
  const clippingPlaneRef = useRef<Plane>(null);
  // 控制器
  const controllerRef = useRef<DualController>();
  // 最新的平面的法向量,
  const meshRef = useRef<Mesh>(null);
  const { renderer } = ctx2.viewer.sceneSetup;
  const xAxis = new Vector3(1, 0, 0);
  const yAxis = new Vector3(0, 1, 0);
  const zAxis = new Vector3(0, 0, 1);

  const schema: RJSFSchema = {
    type: 'object',
    properties: {
      plane: {
        type: 'string',
        format: PickInputRJSFFormat.PickPlane,
      },
      distance: {
        type: 'number',
        title: '距离',
        default: 0,
        format: NumberInputFormat.Length,
      },
      uAngle: {
        type: 'number',
        title: '角度U角',
        minimum: -90,
        maximum: 90,
        default: 0,
        format: NumberInputFormat.Angle,
      },
      vAngle: {
        type: 'number',
        title: '角度V角',
        default: 0,
        format: NumberInputFormat.Angle,
      },
      reverse: {
        type: 'boolean',
        title: '反向',
        default: false,
      },
    },
  };

  const uiSchema: UiSchema = {
    plane: {
      'ui:placeholder': '请选择剖面参考面',
      'ui:autofocus': true,
    },
    distance: {
      'ui:options': {
        showReverse: true,
        onReverseChange: () => {
          // 距离取反了，
          setFormData((prev) => ({
            ...prev,
            distance: prev.distance * -1,
          }));
        },
      },
    },
    uAngle: {
      'ui:options': {
        showReverse: true,
        onReverseChange: () => {
          setFormData((prev) => {
            setUAngleDiff(prev.uAngle * -1 - prev.uAngle);
            return {
              ...prev,
              uAngle: prev.uAngle * -1,
            };
          });
        },
      },
    },
    vAngle: {
      'ui:options': {
        showReverse: true,
        onReverseChange: () => {
          setFormData((prev) => {
            setVAngleDiff(prev.vAngle * -1 - prev.vAngle);
            return {
              ...prev,
              vAngle: prev.vAngle * -1,
            };
          });
        },
      },
    },
  };
  useEffect(() => {
    setFormTouched(true);
    userEvents.emit(SectionViewActivatedEvent, !!formData.plane);

    ctx2.cadScene.assisGroup.clear();
    if (formData.plane) {
      const { normalVec, centerVec, smallestArray } = getPlaneNormalAndCenter(
        formData.plane,
      );
      setAxisSmallestArray(smallestArray);
      // 初始化截面
      clippingPlaneRef.current = new Plane(normalVec.clone(), 0);
      initCreatePlane(normalVec, centerVec);
      applyClippingPlane();
    } else {
      // 没有选中平面的时候，需要重置截面，隐藏控制器
      resetClippingPlane();
      controllerRef.current?.detach();
    }
  }, [formData.plane]);
  useEffect(() => {
    // 前提是创建的平面存在
    if (meshRef.current) {
      updatePlaneDistance();
      // controllerRef.current.translateLocally(planeNormalRef.current.toArray(),formData.distance)
      ctx2.viewer.requestRender();
    }
  }, [formData.distance]);
  useEffect(() => {
    controllerRef.current = new DualController(ctx2);
    const abortController = new AbortController();
    const { signal } = abortController;
    controllerRef.current.addEventListener(
      'transform',
      (e) => {
        setDistanceDiff(0);
        const customEvent = e as CustomEvent<ITransformDetail>;
        const { localTranslation, localRotation, worldDirectionZ } =
          customEvent.detail;

        setFormData((data) => ({
          ...data,
          distance: new Vector3(...localTranslation).dot(worldDirectionZ),
          uAngle: localRotation[1] * (180 / Math.PI),
          vAngle: localRotation[0] * (180 / Math.PI),
        }));
        planeNormalRef.current = worldDirectionZ;

        ctx2.viewer.requestRender();
      },
      { signal },
    );
    return () => {
      controllerRef.current.detach();
      controllerRef.current.destroy();
      abortController.abort();
    };
  }, []);
  useEffect(() => {
    if (Math.abs(formData.uAngle) > 90 || Math.abs(formData.vAngle) > 90) {
      throttledShowWarning('请输入/旋转一个介于 -90 和 90 之间的角度');
    }

    if (meshRef.current) {
      updatePlaneRotation();
      ctx2.viewer.requestRender();
    }
  }, [formData.uAngle, formData.vAngle]);
  // 剖面反转
  useEffect(() => {
    if (meshRef.current) {
      clippingPlaneRef.current.normal.negate();
      clippingPlaneRef.current.constant =
        clippingPlaneRef.current.constant * -1 + 0.1;
      planeNormalRef.current.negate();
      // 更新 mesh 的旋转
      const quaternion = new Quaternion().setFromUnitVectors(
        zAxis, // 假设初始法向量指向 z 轴
        planeNormalRef.current,
      );
      meshRef.current.quaternion.copy(quaternion);
      ctx2.viewer.requestRender();
    }
  }, [formData.reverse]);

  const getPlaneNormalAndCenter = (planeData: pickedFaceData) => {
    let normalVec: Vector3;
    let centerVec: Vector3;
    let smallestArray: string[];
    // 判断pick 的面有没有modelData
    if (!planeData.modelData) {
      // 没有modelData 说明是单独的平面
      // 因为目前的cad 创建的平面都是参考面，中心都是原点
      const { placement } = planeData.objInfo;
      normalVec = new Vector3(
        placement[1][0],
        placement[1][1],
        placement[1][2],
      ).normalize();
      centerVec = new Vector3(
        placement[0][0],
        placement[0][1],
        placement[0][2],
      );
      smallestArray = findTwoSmallestVariables(normalVec);
    } else {
      // 1 拿到picker 面的索引
      const faceIndex = planeData?.idInfo?.i ?? 0;
      const normal = planeData?.modelData?.faces[faceIndex].normals?.[0];
      normalVec = new Vector3(normal[0], normal[1], normal[2]);
      // pick平面的时候计算和三个平面的点积，取最小的两个值，根据最小的所在的轴，计算u v 旋转角度
      smallestArray = findTwoSmallestVariables(normalVec);
      // pick 面的中心坐标
      const centerPostion = planeData?.modelData?.faces[faceIndex].center;
      centerVec = new Vector3(
        centerPostion[0],
        centerPostion[1],
        centerPostion[2],
      );
    }

    return { normalVec, centerVec, smallestArray };
  };
  // 三个数取最小的两个
  const findTwoSmallestVariables = (initPlaneNormal: Vector3) => {
    const cosThetaX = Math.abs(initPlaneNormal.dot(xAxis));
    const cosThetaY = Math.abs(initPlaneNormal.dot(yAxis));
    const cosThetaZ = Math.abs(initPlaneNormal.dot(zAxis));
    const values = [
      { value: cosThetaX, name: 'X' },
      { value: cosThetaY, name: 'Y' },
      { value: cosThetaZ, name: 'Z' },
    ];
    values.sort((a, b) => a.value - b.value);
    return [values[0].name, values[1].name];
  };
  // 给planeGeometry 添加边
  const addPlaneEdges = (vertices: Vector3[]) => {
    const geometry = new BufferGeometry().setFromPoints(vertices);
    const lineMaterial = new LineBasicMaterial({
      color: 0x815cfa,
      linewidth: 3,
    });
    geometry.index = new BufferAttribute(new Uint16Array([0, 1, 3, 2, 0]), 1);
    const lineSegment = new Line(geometry, lineMaterial);
    meshRef.current.add(lineSegment);
  };
  //  初始化平面
  const initCreatePlane = (normalVec: Vector3, centerVec: Vector3) => {
    // 创建平面几何体
    const planeObj = new PlaneGeometry(width, height);
    const material = new MeshBasicMaterial({
      color: 0x815cfa,
      side: DoubleSide,
      transparent: true,
      opacity: 0.05,
    });
    meshRef.current = new Mesh(planeObj, material);
    // 给平面加边
    const vertices = planeObj.vertices;
    addPlaneEdges(vertices);
    // 初始平面法向量
    planeNormalRef.current = normalVec.clone();
    const matrix4 = new Matrix4();
    // 创建平移矩阵
    const translationMatrix = new Matrix4().makeTranslation(
      centerVec.x,
      centerVec.y,
      centerVec.z,
    );
    // 创建旋转矩阵，使平面法向量与 Z 轴对齐(默认)
    const quaternion = new Quaternion().setFromUnitVectors(
      zAxis,
      planeNormalRef.current,
    );
    const rotationMatrix = new Matrix4().makeRotationFromQuaternion(quaternion);
    // 组合平移和旋转矩阵
    matrix4.multiplyMatrices(translationMatrix, rotationMatrix);
    // 应用变换矩阵到 mesh
    meshRef.current.applyMatrix4(matrix4);

    // 若为零件上的平面，则应用一级零件的变换矩阵数组
    const matrixs =
      allParts.get(formData.plane.idInfo.o?.split(':')[0])?.matrix4Array ?? [];

    applyMatrix4ArrayToObject(meshRef.current, matrixs);

    // mesh变换后反过来求planeNormalRef
    const currentQuaternion = new Quaternion();
    meshRef.current.getWorldQuaternion(currentQuaternion);

    planeNormalRef.current = zAxis
      .applyQuaternion(currentQuaternion)
      .normalize();

    setClippingPlane();

    controllerRef.current.attach(meshRef.current);
    controllerRef.current.updateGizmoVisibility({
      translate: {
        showX: false,
        showY: false,
      },
      rotate: {
        showZ: false,
      },
    });
    ctx2.cadScene.assisGroup.add(meshRef.current);
    ctx2.viewer.requestRender();
  };
  const updatePlaneDistance = () => {
    if (distanceDiff) {
      const newCenter = meshRef.current.position
        .clone()
        .add(planeNormalRef.current.clone().setLength(distanceDiff));
      meshRef.current.position.copy(newCenter);
    }
    setClippingPlane();
  };
  const updatePlaneRotation = () => {
    const quaternion = getUVRotation();
    const newNormalVec = planeNormalRef.current.applyQuaternion(quaternion);
    planeNormalRef.current.copy(newNormalVec);
    meshRef.current.applyQuaternion(quaternion);

    setClippingPlane();
  };
  // 设置截面面
  const setClippingPlane = () => {
    const planeConstant = -planeNormalRef.current.dot(meshRef.current.position);
    clippingPlaneRef.current.constant = planeConstant + 0.05;
    clippingPlaneRef.current.normal.copy(planeNormalRef.current);
  };

  // 应用截面
  const applyClippingPlane = () => {
    renderer.clippingPlanes = [clippingPlaneRef.current];
  };
  const resetClippingPlane = () => {
    renderer.clippingPlanes = [];
    renderer.localClippingEnabled = false;
  };
  const handleFormDataChange = (data) => {
    const { distance, uAngle, vAngle, plane } = data;
    setFormData(data);
    if (distance !== formData.distance) {
      setDistanceDiff(distance - formData.distance);
    }
    if (uAngle !== formData.uAngle) {
      setUAngleDiff(uAngle - formData.uAngle);
    }
    if (vAngle !== formData.vAngle) {
      setVAngleDiff(vAngle - formData.vAngle);
    }
    // 切换不同平面的置表单值
    if (formData.plane && formData.plane?.meshId !== data.plane?.meshId) {
      setFormData({
        plane: plane,
        distance: 0,
        uAngle: 0,
        vAngle: 0,
        reverse: false,
      });
    }
  };

  // 计算绕剖面绕哪个轴做uv 旋转
  const getUVRotation = () => {
    const quaternion: Quaternion = new Quaternion();
    const updataRotationQuaternion = (
      quaternion: Quaternion,
      axis: Vector3,
      angle: number,
    ) => {
      const rotationAxis = axis
        .clone()
        .cross(planeNormalRef.current)
        .normalize();
      return quaternion.setFromAxisAngle(rotationAxis, angle);
    };
    let axis1: Vector3;
    let axis2: Vector3;
    if (
      axisSmallestArray.includes(AxisType.X) &&
      axisSmallestArray.includes(AxisType.Y)
    ) {
      axis1 = xAxis;
      axis2 = yAxis;
    } else if (
      axisSmallestArray.includes(AxisType.X) &&
      axisSmallestArray.includes(AxisType.Z)
    ) {
      axis1 = xAxis;
      axis2 = zAxis;
    } else {
      axis1 = yAxis;
      axis2 = zAxis;
    }
    if (uAngleDiff) {
      updataRotationQuaternion(
        quaternion,
        axis1,
        -uAngleDiff * (Math.PI / 180),
      );
    }
    if (vAngleDiff) {
      updataRotationQuaternion(
        quaternion,
        axis2,
        -vAngleDiff * (Math.PI / 180),
      );
    }

    setUAngleDiff(0);
    setVAngleDiff(0);

    return quaternion;
  };
  const handleDialogClose = () => {
    userEvents.emit(SectionViewActivatedEvent, false);
    resetClippingPlane();

    onClose();
  };
  useCommand('cmdSectionView', {
    onClose: handleDialogClose,
  });
  const handleSubmit = () => {
    onClose();
  };

  // 如果cmdSectionView.activated，但没有选中平面，说明是上次activated状态后已关闭对话框
  // 这是重新点击命令按钮触发的，执行关闭剖视图状态的操作，不显示表单
  if (!formTouched && cmdSectionView.activated && !formData.plane) {
    handleDialogClose();

    return null;
  }

  return (
    <PickableCommandForm
      isFormDataValid={!!formData.plane}
      className='sectionView'
      schema={schema}
      title={title}
      extraUiSchema={uiSchema}
      onClose={handleDialogClose}
      formData={formData}
      onChange={handleFormDataChange}
      onSubmit={handleSubmit}
    />
  );
}

export const cmdSectionView: BaseCommand & { activated: boolean } = {
  id: 'cmdSectionView',
  label: 'command.section-view',
  icon: 'commands/product/section-view',
  create: (label?: string, onClose?: () => void) => (
    <SectionView
      onClose={onClose}
      title={i18n.t(label)}
    />
  ),
  activated: false,
};
